feat(website): sell plain-language prompts and cross-model quality - #1202
feat(website): sell plain-language prompts and cross-model quality#1202vivek7405 wants to merge 5 commits into
Conversation
The /why-webjs page argued model-agnosticism only from the model side: any model can read the source, so no training data is required. That leaves out the half a reader actually cares about. Someone with no technical background can write an ordinary request and still get correct routing, a real schema, server code on the right side of the boundary, and a coherent design system, because the conventions decide the shape before the prompt is written. Adds a section making that argument with a prompt-to-files pairing, and extends the any-model section to say the output quality holds across model sizes rather than only that a small model can participate.
vivek7405
left a comment
There was a problem hiding this comment.
These two pages were arguing model-agnosticism from the model side only, and the reader-side half is the better argument, so I like where this is going. Two things I want changed before it ships.
The design-system claim is the one that actually bothers me. We say in AGENTS.md that there is no design gate and that taste is the agent's job, so promising that the design comes out consistent across models is a claim the framework does not back. Tokens and the kit are the real mechanism and the copy should say so instead of implying convergence falls out of reading the source.
The other is the what-is-webjs card. Folding the sentence into an existing capability was the right instinct, but that cell is now more than twice its row-mates and h-full makes it set the row height, so it buys dead space inside the two cells beside it. The rest is wording: the meta description grew past every place it renders, the new paragraph repeats the one above it, and the two demo panels do not answer each other.
Review caught the design-system half of the any-model paragraph as an overclaim. AGENTS.md says the opposite outright: there is no design gate, taste is the agent's job, and an app sets its own palette in the root layout. The paragraph now credits the real mechanism, design tokens set in one place, and says plainly that taste is the reader's to direct. Also from review: the meta description had grown to 444 chars with the new clause sitting past every truncation point it feeds, so the claim now leads it instead; the what-is-webjs card is back to roughly its pre-change length, since h-full made the longest cell pin its row and buy dead space in both neighbours; the two demo panels now answer each other (the prompt no longer says page while the result shows two); the static file listing is labelled files rather than terminal; and the four scrollable pre blocks on the page carry role=region, so the aria-label each already had is one ARIA permits.
vivek7405
left a comment
There was a problem hiding this comment.
Two of these are damage I did in the fix commit, which is the annoying kind. Trimming the what-is-webjs card to win back grid height cut the words that were carrying the accuracy: the framework claim is now flatly false against what core actually resolves to, and the AGENTS.md sentence now says five tools read a file that three of them do not. Length is the wrong thing to have optimized there.
The test gap is the one I most want closed. The demo grid is the bulk of what this PR adds and the whole thing could be deleted without reddening anything, which makes the coverage claim in the body hollow.
Path-level, since the lines are outside the diff: the role fix needs to go all the way or not at all. It landed on the four blocks on why-webjs and nowhere else, so the same prohibited-name construct is still on the codeWindow helper at app/what-is-webjs/page.ts:210 and on six blocks in app/page.ts (:106, :219, :235, :387, :396, plus its own helper). app/page.ts:235 is the worst of them, an aria-label with no tabindex and no role at all. Doing four and leaving ten is the worst of both ends.
The previous commit traded accuracy for grid height and lost. Trimming the what-is-webjs card cut the two phrases that were carrying it: without 'with JSDoc in node_modules' the framework claim is flatly false, since core resolves its default entry to dist/webjs-core.js, and without 'read from one source' the AGENTS.md sentence claims five tools read a file three of them do not. Both phrases are back, and the length is won by dropping a convention detail instead. The demo grid was also entirely unasserted, so it could have been deleted with every test in the file still green. It now pins both panels, their correspondence, and the accessible name each block carries, plus a snippet-window assertion mirroring the one on the sibling page. The role=region fix goes site-wide rather than stopping at one page: the prohibited-name construct was on ten more blocks. The one block that is not focusable loses its name instead of gaining a role, since nothing can focus it to hear one.
|
Follow-up: the aria-label fix went site-wide rather than stopping at one page The path-level note about the role fix is handled in Thirteen scrollable blocks get |
vivek7405
left a comment
There was a problem hiding this comment.
Round three and two of these are mine again, both from the sweep I did last commit.
The negative assertion I added is the worst of them. It is tautological, so the guarantee it is named for is not held by anything, and I wrote it while fixing a test gap, which is the exact failure the gap fix was supposed to close.
The role sweep also traded one a11y problem for another in two places: two cards now share a landmark name, and the block I chose to strip rather than promote is the one that actually has the keyboard defect. I stripped the name and left the reason the name was unreachable.
And the framework claim is still not right even after restoring the words round two asked for. The scoping was necessary but not sufficient: the bundle is in node_modules as well, so the comparative is the part that has to go, not the qualifier.
…rades The negative assertion added last commit could never match. The prompt's chevron sits in its own span, so a regex requiring it adjacent to the prose is false whether or not the regression is present, which left the guarantee it is named for held by nothing. Plain substring now. The role sweep also traded one problem for another twice. Two template cards shared an aria-label, so promoting both to landmarks produced a duplicate-named pair where previously the prohibited name was simply ignored; each is now named for the template it shows. And the block the sweep stripped rather than promoted is the one with the real defect: it scrolls horizontally with no tabindex, so revealing it hands a keyboard user a region they cannot reach. It gets the focus stop and the name. The framework claim needed the comparative removed, not the qualifier restored: dist/webjs-core.js is what the main entry resolves to and it lives in node_modules too, so scoping the sentence there never made 'rather than a compiled bundle' true. The readable source is the part that is true, so that is all it claims now.
vivek7405
left a comment
There was a problem hiding this comment.
Two of these say my last commit was the regression, and having checked both premises myself I agree.
The compiled-bundle edit is the clearer mistake. I changed one of five instances of a claim that root AGENTS.md itself makes, inside a PR about two pages of copy, and the result is that the page now argues with itself and with its own structured data. Whether that claim is right across the site is a real question, but it is not this PR's question, so that clause goes back to exactly what it said before I touched it and the PR takes no position.
The focus stop is the same shape of error. I accepted "it scrolls" without measuring, and the sample is one short line that does not scroll at any width anyone will see. That block goes back to carrying nothing.
The two test findings are fair and both are about the assertion holding the thing its message claims, which is the same problem as the tautology, one step subtler. The uncovered sweep on the other two pages needs pinning too.
…ite-wide Two corrections from the last round were themselves the regression, and checking both premises confirmed it. The compiled-bundle clause is back to exactly its pre-change wording. It appears five times across the site and in root AGENTS.md, so editing one instance left the page disagreeing with its own FAQ, with the FAQPage schema that FAQ feeds, and with /why-webjs. Whether the claim holds everywhere is a real question and not this PR's; the PR now takes no position on it. The toggled usage block is back to carrying nothing. Its content is one 37-character line that never becomes a scroll container at any width a reader will use, so the tabindex added for it was a permanent tab stop on inert content rather than the rescue of an unreachable region. The negative prompt assertion was a tripwire for one literal under a message promising much more, so it now asserts the property the section actually claims: the prompt panel contains no framework vocabulary at all, which no rewording walks past. And the name rule is pinned across all three pages that render code blocks, in its own file, since the rule belongs to the site rather than to one page and covering one page is how the other two drifted.
|
Follow-up: the coverage gap the sweep left, and the one claim this PR stopped short of Two things from the last round worth writing down rather than leaving in the threads. The accessibility rule now lives in The other is a claim I deliberately did not settle here. The sentence "plain JavaScript with JSDoc in node_modules rather than a compiled bundle" is on this card, in the FAQ answer that feeds the |
vivek7405
left a comment
There was a problem hiding this comment.
This is the fifth round and it found real things again, so I am stopping here rather than starting a sixth. The pattern is the problem, not any one finding: five rounds running where a correction introduced its own defect is signal to hand this back rather than keep going alone.
The finding that matters most is that one of last round's two reverts was not a revert. main carries an aria-label on that block and this branch deletes it, so the PR's net effect there is removing an accessible name that already existed, which is the opposite of what both the commit message and the PR body say. I verified that against main directly.
The rest cluster in the tests I added, and they share one shape: an assertion whose message promises a property while the check tests a proxy for it. A substring that matches inside other words, a set membership standing in for one specific element, a premise about scroll behaviour pinned as an invariant. That is the third round running where the test defect is subtler than the last, which is its own kind of signal.
Path-level: the PR establishes a site-wide markup convention and enforces it with a test, but website/AGENTS.md says nothing about it, so the only statement of the rule is the test's own header.
Leaving these open and the PR in draft.
| <like-button count="3"></like-button> | ||
| </div> | ||
| <pre class="hidden group-has-[:checked]/stage:block flex-1 m-0 p-5 overflow-x-auto font-mono text-xs leading-[1.72] text-left" aria-label="like-button usage"><code>${highlight(USAGE_SAMPLE)}</code></pre> | ||
| <pre class="hidden group-has-[:checked]/stage:block flex-1 m-0 p-5 overflow-x-auto font-mono text-xs leading-[1.72] text-left"><code>${highlight(USAGE_SAMPLE)}</code></pre> |
There was a problem hiding this comment.
This is not the revert I called it. main carries aria-label="like-button usage" on this block, so the PR's net effect here is deleting an accessible name the block already had, while the same PR resolves the identical construct on four other blocks by adding a role instead. Verified against main.
|
|
||
| test('a code block with no name needs no role, so it adds no landmark', async () => { | ||
| // The home page's toggled usage block holds one short line that never becomes | ||
| // a scroll container at a real viewport width. It carries no name and no |
There was a problem hiding this comment.
The premise is wrong at the width WCAG reflow names. The sample is about 266px of text and the content box at a 320px viewport is about 230px, so it does overflow below roughly 356px. The test then pins that wrong premise as an invariant, so the block cannot be given a name or a focus stop without editing this test.
| // a permanent tab stop on content nothing can interact with. | ||
| const tags = preTags(await renderToString(Home())); | ||
| const unnamed = tags.filter((t) => !nameOf(t)); | ||
| assert.ok(unnamed.length > 0, 'the home page still renders an unnamed code block'); |
There was a problem hiding this comment.
This documents one block and observes a set. It passes if the usage block gains a name while any other unnamed block exists, which is exactly when the property it describes stops being observed, and it reds with this message if that block is legitimately named.
| // one historical phrasing, which any reworded regression would walk past. | ||
| const prompt = out.slice(out.indexOf('aria-label="A plain-language prompt'), out.indexOf('Where the conventions put it')); | ||
| assert.ok(prompt.includes('Let customers'), 'the slice really is the prompt panel'); | ||
| for (const jargon of ['page.ts', '.server.ts', 'route', 'component', 'schema', 'action']) { |
There was a problem hiding this comment.
Raw substring, so it does not assert the property its message names. action matches inside transaction and interaction, route inside routine. Plain-language copy like record the transaction would red under a message saying the prompt mentions action, which is copy the section is supposed to encourage.
| // The prose above the demo carries the argument, but the two windows are what | ||
| // SHOW it, and they were previously unasserted: the whole grid could be | ||
| // deleted with every test in this file still green. These pin both panels, | ||
| // their correspondence, and the accessible name each scrollable block needs. |
There was a problem hiding this comment.
Stale. The name assertions moved to pre-block-a11y.test.ts; the aria-label survives here only as a slice index and nothing in this test checks a name or a role.
| for (const file of ['app/book/page.ts', 'app/staff/bookings/page.ts', 'modules/bookings/actions/create.server.ts', 'db/schema.server.ts']) { | ||
| assert.ok(out.includes(file), `the file panel answers the prompt with ${file}`); | ||
| } | ||
| assert.ok(out.includes('<span class="ml-2 font-mono font-medium text-xs leading-none text-fg-subtle">files</span>'), 'the file listing is labelled files, not terminal'); |
There was a problem hiding this comment.
Hardcodes the full WINNAME class string, which the page documents as kept in lockstep with app/page.ts, so a design-system edit reds this test with a message about a word that did not change.
| const title = 'Why WebJs - The Framework Your AI Agent Already Understands'; | ||
| const description = | ||
| 'WebJs is a full-stack JavaScript framework that serves the framework source and your app code exactly as written, so any AI model can read the whole stack from node_modules, reason about it, and debug it. No training data required, no single blessed model. Built on the web components, HTML, and JavaScript every model already knows.'; | ||
| 'WebJs is a full-stack JavaScript framework you describe in plain language, because the file conventions land an app in the right shape whatever model you use. Nothing is hidden behind a build step, so any AI model reads the framework source from node_modules, reasons about the whole stack, and debugs it. No training data required, no single blessed model, on the web components and HTML every model already knows.'; |
There was a problem hiding this comment.
Attaches the claim to the wrong noun. What you describe in plain language is the app, not the framework. This is the exact sentence the snippet-window test pins.
| * rule is a property of the SITE, not of one page. The pages here were fixed | ||
| * together, and pinning them together is what stops the next one from drifting | ||
| * back: the sweep that fixed them originally covered one page, and the other | ||
| * two went unobserved until a review caught it. |
There was a problem hiding this comment.
Narrates this PR's review history in permanent source. The rule is worth stating; how it was arrived at is not.
Closes #1201
The
/why-webjspage argued model-agnosticism only from the model side: any model can read the whole framework fromnode_modules, so no training data and no single blessed model are required. That leaves out the half a reader actually cares about. Someone with no technical background can write an ordinary request and still get back correct routing, a real schema, server code on the right side of the.server.tsboundary, and a coherent design system, because the conventions decide the shape of the app before the prompt is written. This adds a section making that argument, and extends the existing any-model section to say the output quality holds across model sizes rather than only that a small model can participate.What changed
website/app/why-webjs/page.ts: a new "Describe what you want in plain language" section between the reason grid and the any-model section, built from the page's existing terminal-window language. It pairs a plain-English prompt with the files the conventions put that work in, so the argument is shown rather than asserted. The any-model section gains a paragraph tying model-agnosticism to output quality, and the meta description leads with the plain-language claim.website/app/what-is-webjs/page.ts: one short echo folded into the existing "AI-first, readable end to end" capability rather than a seventh card, which would leave the three-column grid ragged. Worded differently from/why-webjsso the two pages do not chase the same phrasing.website/app/page.ts: an accessibility fix that review pulled in, unrelated to the copy. A<pre>maps to rolegeneric, where ARIA prohibits an author-supplied name, so thearia-labelthese code blocks already carried was a name no spec-following screen reader would announce. Every<pre>that already carried such a name now carriesrole="region", and the two that shared a label are named apart so they do not collapse into an ambiguous landmark pair. Fixing four blocks on one page and leaving ten identical ones would have left the site doing the same thing two ways.website/test/ssr/why-webjs-ssr.test.ts: assertions on the new section's prose, on both demo panels, on the prompt panel containing no framework vocabulary, and on the description answering inside the 160-char snippet window.website/test/ssr/pre-block-a11y.test.ts: the accessible-name rule pinned across all three pages that render code blocks, since it is a property of the site rather than of one page, and covering one page is how the other two drifted.Deliberately excluded
app/page.tschange above is the accessibility sweep, not positioning.Test plan
webjs checkclean00487b5a: deleting the demo grid reds the new grid testb7928acf: duplicating the two card labels reds the landmark-uniqueness test, dropping the role from thecodeWindowhelper reds the role test, and putting a framework noun in the prompt reds the jargon testDoc surfaces: website copy plus one accessibility fix. No
packages/*/srcchange, so the framework doc surfaces, the scaffold, the MCP, the editor plugins, and the changelog are all N/A. The dogfood boot check is N/A beyond the website itself, which is the app being edited and is covered by its own suite.